package cn.xuewenbao.edu.controller;


import cn.xuewenbao.edu.entity.ESEntity;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.client.indices.GetIndexResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.jeecg.common.api.vo.Result;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @Auther: mafayu
 */
@RestController
@RequestMapping("/edu/es")
@Api(tags = "es搜索")
@Slf4j
public class ESController {


    @Value("${elasticsearch.clientIp}")
    private String clientIp;

    @Value("${elasticsearch.port}")
    private String port;

    @Value("${elasticsearch.password}")
    private String password;

    @Value("${elasticsearch.username}")
    private String userName;


    @ApiOperation("添加索引")
    @PutMapping("addIndex/{index}")
    public Result<Boolean> addIndex(@PathVariable String index) {
        Result<Boolean> result = new Result<>();

        HttpHost httpHosts = new HttpHost(clientIp, Integer.parseInt(port), "https");

        RestClientBuilder restClientBuilder = RestClient.builder(httpHosts)
                .setHttpClientConfigCallback(httpClientBuilder -> {
                    CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
                    credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(userName, password));
                    return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
                });
        RestHighLevelClient client = new RestHighLevelClient(restClientBuilder);

        CreateIndexRequest request = new CreateIndexRequest(index);
        try {
            CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);
            if (response.isAcknowledged()) {
                result.setResult(true);
            }
        } catch (IOException e) {
            log.error(e.toString());
        }
        try {
            client.close();
        } catch (IOException e) {
            log.error(e.toString());
        }
        return result;
    }

    @ApiOperation("查询索引")
    @GetMapping("searchIndex/{index}")
    public Result<String> searchIndex(@PathVariable String index) {
        Result<String> result = new Result<>();

        HttpHost httpHosts = new HttpHost(clientIp, Integer.parseInt(port), "https");

        RestClientBuilder restClientBuilder = RestClient.builder(httpHosts)
                .setHttpClientConfigCallback(httpClientBuilder -> {
                    CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
                    credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(userName, password));
                    return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
                });
        RestHighLevelClient client = new RestHighLevelClient(restClientBuilder);

        GetIndexRequest request = new GetIndexRequest(index);
        try {
            GetIndexResponse response = client.indices().get(request, RequestOptions.DEFAULT);
            result.setResult(response.getMappings().toString());
        } catch (IOException e) {
            log.error(e.toString());
        }
        try {
            client.close();
        } catch (IOException e) {
            log.error(e.toString());
        }
        return result;
    }

    @ApiOperation("删除索引")
    @DeleteMapping("deleteIndex/{index}")
    public Result<Boolean> deleteIndex(@PathVariable String index) {
        Result<Boolean> result = new Result<>();

        HttpHost httpHosts = new HttpHost(clientIp, Integer.parseInt(port), "https");

        RestClientBuilder restClientBuilder = RestClient.builder(httpHosts)
                .setHttpClientConfigCallback(httpClientBuilder -> {
                    CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
                    credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(userName, password));
                    return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
                });
        RestHighLevelClient client = new RestHighLevelClient(restClientBuilder);

        DeleteIndexRequest request = new DeleteIndexRequest(index);
        try {
            AcknowledgedResponse response = client.indices().delete(request, RequestOptions.DEFAULT);
            if (response.isAcknowledged()) {
                result.setResult(true);
            }
        } catch (IOException e) {
            log.error(e.toString());
        }
        try {
            client.close();
        } catch (IOException e) {
            log.error(e.toString());
        }
        return result;
    }

    @ApiOperation("添加文档")
    @PutMapping("addDoc/{index}")
    public Result<Boolean> addDoc(@PathVariable String index,
                                  @RequestBody ESEntity entity) throws IOException {
        Result<Boolean> result = new Result<>();

        HttpHost httpHosts = new HttpHost(clientIp, Integer.parseInt(port), "https");

        RestClientBuilder restClientBuilder = RestClient.builder(httpHosts)
                .setHttpClientConfigCallback(httpClientBuilder -> {
                    CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
                    credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(userName, password));
                    return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
                });
        RestHighLevelClient client = new RestHighLevelClient(restClientBuilder);

        //插入数据DOC
        IndexRequest request = new IndexRequest();
        request.index(index).id(entity.getId());


        ObjectMapper mapper = new ObjectMapper();
        try {
            final String s = mapper.writeValueAsString(entity);
            request.source(s, XContentType.JSON);

            IndexResponse response = client.index(request, RequestOptions.DEFAULT);

            if (response.getResult() != null) {
                result.setResult(true);
            }
        } catch (JsonProcessingException e) {
            log.error(e.toString());
        }

        try {
            client.close();
        } catch (IOException e) {
            log.error(e.toString());
        }
        return result;
    }

    @ApiOperation("查询文档")
    @GetMapping("searchDoc/{index}/{id}")
    public Result<String> searchDoc(@PathVariable String index,
                                    @PathVariable String id) throws IOException {
        Result<String> result = new Result<>();

        HttpHost httpHosts = new HttpHost(clientIp, Integer.parseInt(port), "https");

        RestClientBuilder restClientBuilder = RestClient.builder(httpHosts)
                .setHttpClientConfigCallback(httpClientBuilder -> {
                    CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
                    credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(userName, password));
                    return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
                });
        RestHighLevelClient client = new RestHighLevelClient(restClientBuilder);

        //查询文档
        GetRequest request = new GetRequest();
        request.index(index).id(id);

        GetResponse response = client.get(request, RequestOptions.DEFAULT);

        result.setResult(response.getSource().toString());


        client.close();

        return result;
    }

    @ApiOperation("删除文档")
    @DeleteMapping("deleteDoc/{index}/{id}")
    public Result<String> deleteDoc(@PathVariable String index,
                                    @PathVariable String id) throws IOException {
        Result<String> result = new Result<>();

        HttpHost httpHosts = new HttpHost(clientIp, Integer.parseInt(port), "https");

        RestClientBuilder restClientBuilder = RestClient.builder(httpHosts)
                .setHttpClientConfigCallback(httpClientBuilder -> {
                    CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
                    credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(userName, password));
                    return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
                });
        RestHighLevelClient client = new RestHighLevelClient(restClientBuilder);

        DeleteRequest request = new DeleteRequest();
        request.index(index).id(id);

        DeleteResponse response = client.delete(request, RequestOptions.DEFAULT);

        result.setResult(response.toString());
        client.close();
        return result;
    }

    @ApiOperation("查询索引中的全部文档")
    @GetMapping("searchAllDoc/{index}")
    public Result<List<String>> searchAllDoc(@PathVariable String index) throws IOException {
        Result<List<String>> result = new Result<>();

        HttpHost httpHosts = new HttpHost(clientIp, Integer.parseInt(port), "https");

        RestClientBuilder restClientBuilder = RestClient.builder(httpHosts)
                .setHttpClientConfigCallback(httpClientBuilder -> {
                    CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
                    credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(userName, password));
                    return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
                });
        RestHighLevelClient client = new RestHighLevelClient(restClientBuilder);

        //查询文档
        SearchRequest request = new SearchRequest();
        request.indices(index);

        SearchSourceBuilder builder = new SearchSourceBuilder();
        builder.query(QueryBuilders.matchAllQuery());
        //分页查询
//        builder.from(0);
//        builder.size(1);
        //排序
        //builder.sort("age", SortOrder.ASC);
        //过滤字段
        String[] includes = {"id"};
        String[] excludes = {};
        builder.fetchSource(includes, excludes);
        request.source(builder);

        SearchResponse response = client.search(request, RequestOptions.DEFAULT);

        List<String> list = new ArrayList<>();

        SearchHits hits = response.getHits();

        for (SearchHit hit : hits) {
            list.add(hit.getSourceAsString());
        }
        client.close();
        result.setResult(list);
        return result;
    }

    @ApiOperation("条件查询索引中的文档")
    @GetMapping("searchAnyDoc/{index}/{str}")
    public Result<List<String>> searchAnyDoc(@PathVariable String index,
                                             @PathVariable String str) throws IOException {
        Result<List<String>> result = new Result<>();

        HttpHost httpHosts = new HttpHost(clientIp, Integer.parseInt(port), "https");

        RestClientBuilder restClientBuilder = RestClient.builder(httpHosts)
                .setHttpClientConfigCallback(httpClientBuilder -> {
                    CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
                    credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(userName, password));
                    return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
                });
        RestHighLevelClient client = new RestHighLevelClient(restClientBuilder);

        //查询文档
        SearchRequest request = new SearchRequest();
        request.indices(index);

        SearchSourceBuilder builder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(QueryBuilders.matchQuery("title", str));
        boolQueryBuilder.should(QueryBuilders.matchQuery("title", str));
        //范围查询
//        RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("age");
        builder.query(boolQueryBuilder);

        request.source(builder);

        SearchResponse response = client.search(request, RequestOptions.DEFAULT);

        List<String> list = new ArrayList<>();

        SearchHits hits = response.getHits();

        for (SearchHit hit : hits) {
            list.add(hit.getSourceAsString());
        }
        client.close();
        result.setResult(list);
        return result;
    }

    @ApiOperation("模糊查询索引中的文档")
    @GetMapping("searchSelectDoc/{index}/{str}/{pageNo}/{pageNum}")
    public Result<List<Map<String, Object>>> searchSelectDoc(@PathVariable String index,
                                                             @PathVariable String str,
                                                             @PathVariable Integer pageNo,
                                                             @PathVariable Integer pageNum) throws IOException {
        Result<List<Map<String, Object>>> result = new Result<>();

        HttpHost httpHosts = new HttpHost(clientIp, Integer.parseInt(port), "https");

        RestClientBuilder restClientBuilder = RestClient.builder(httpHosts)
                .setHttpClientConfigCallback(httpClientBuilder -> {
                    CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
                    credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(userName, password));
                    return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
                });
        RestHighLevelClient client = new RestHighLevelClient(restClientBuilder);

        //查询文档
        SearchRequest request = new SearchRequest();
        request.indices(index);

        SearchSourceBuilder builder = new SearchSourceBuilder();

//        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//        //分词查询
//        boolQueryBuilder.should(QueryBuilders.queryStringQuery(str).field("title").field("content").field("userName").field("description"));


        //高亮查询
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.preTags("<span style='color:red;'>");
        highlightBuilder.postTags("</span>");// 设置标签后缀
        highlightBuilder.field("title").field("content").field("userName").field("description");

        builder.highlighter(highlightBuilder);

        //分页查询
        builder.from(pageNo);
        builder.size(pageNum);
        builder.query(QueryBuilders.queryStringQuery(str).field("title").field("content").field("userName").field("description"));


        request.source(builder);

        SearchResponse response = client.search(request, RequestOptions.DEFAULT);

        List<Map<String, Object>> list = new ArrayList<>();

        SearchHits hits = response.getHits();

        for (SearchHit hit : hits) {
            //获取高亮字段
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            HighlightField titleField = highlightFields.get("title");
            HighlightField contentField = highlightFields.get("content");
            HighlightField userNameField = highlightFields.get("userName");
            HighlightField descriptionField = highlightFields.get("description");
            Map<String, Object> source = hit.getSourceAsMap();
            if (titleField != null) {
                Text[] fragments = titleField.fragments();
                String name = "";
                for (Text text : fragments) {
                    name += text;
                }
                source.put("title", name);  //高亮字段替换掉原本的内容
            }
            if (contentField != null) {
                Text[] fragments = contentField.fragments();
                String name = "";
                for (Text text : fragments) {
                    name += text;
                }
                source.put("content", name);  //高亮字段替换掉原本的内容
            }
            if (userNameField != null) {
                Text[] fragments = userNameField.fragments();
                String name = "";
                for (Text text : fragments) {
                    name += text;
                }
                source.put("userName", name);  //高亮字段替换掉原本的内容
            }
            if (descriptionField != null) {
                Text[] fragments = descriptionField.fragments();
                String name = "";
                for (Text text : fragments) {
                    name += text;
                }
                source.put("description", name);  //高亮字段替换掉原本的内容
            }
            list.add(source);
        }
        client.close();
        result.setResult(list);
        return result;
    }

}
